<template>
    <div class="base-search-form">
        <div
            class="search-form-wrapper"
            :style="{ '--grid-template-column-num': gridTemplateColumnNum }"
        >
            <div
                class="search-form-item"
                v-for="(item, index) in formList"
                :key="item.prop ? item.prop + index : index"
                v-show="needShow(item, index)"
                v-bind="item"
            >
                <template v-if="item.render">
                    <BaseRender
                        :render="item.render"
                        v-bind="{ item, formData, formList }"
                    ></BaseRender>
                </template>
                <template v-else-if="item.prop && slots[item.prop]">
                    <slot :name="item.prop" v-bind="{ item, formData, formList }"></slot>
                </template>
                <template v-else-if="item.component">
                    <component
                        :is="item.component"
                        v-model="formData[item.prop]"
                        v-bind="item.componentProps"
                    ></component>
                </template>
                <template v-else-if="item.type === `input`">
                    <el-input
                        v-model="formData[item.prop]"
                        :class="[`search-form-item-input`]"
                        v-bind="filterBindMap.defaultFilterBind(item.props)"
                    >
                        <template v-for="(value, key) in item.slots || {}" :key="key" #[key]>
                            <BaseRender
                                :render="value"
                                v-bind="{ item, formData, formList }"
                            ></BaseRender>
                        </template>
                    </el-input>
                </template>
                <template v-else-if="item.type === `select`">
                    <BaseSelect
                        v-model="formData[item.prop]"
                        :class="[`search-form-item-input`]"
                        v-bind="filterBindMap.defaultFilterBind(item.props)"
                    >
                        <template v-for="(value, key) in item.slots || {}" :key="key" #[key]>
                            <BaseRender
                                :render="value"
                                v-bind="{ item, formData, formList }"
                            ></BaseRender>
                        </template>
                    </BaseSelect>
                </template>
                <template v-else-if="item.type === `tree-select`">
                    <el-tree-select
                        v-model="formData[item.prop]"
                        :class="[`search-form-item-input`]"
                        v-bind="filterBindMap.defaultFilterBind(item.props)"
                    >
                        <template v-for="(value, key) in item.slots || {}" :key="key" #[key]>
                            <BaseRender
                                :render="value"
                                v-bind="{ item, formData, formList }"
                            ></BaseRender>
                        </template>
                    </el-tree-select>
                </template>
                <template v-else-if="item.type === `date-picker`">
                    <el-date-picker
                        v-model="formData[item.prop]"
                        :class="[
                            isRangePicker(item)
                                ? `search-form-item-range-picker`
                                : `search-form-item-input`,
                        ]"
                        v-bind="filterBindMap.defaultFilterBind(item.props)"
                    >
                        <template v-for="(value, key) in item.slots || {}" :key="key" #[key]>
                            <BaseRender
                                :render="value"
                                v-bind="{ item, formData, formList }"
                            ></BaseRender>
                        </template>
                    </el-date-picker>
                </template>
            </div>
        </div>
        <div class="toggle-box" v-if="allColumnNum > 4">
            <div
                class="toggle-btn"
                :class="{ 'is-expand': isExpand }"
                @click="toggleExpand(!isExpand)"
            >
                <span>{{ isExpand ? '收起' : '展开' }}</span>
                <i class="i-ep-arrow-down"></i>
            </div>
        </div>
        <div class="button-list">
            <BaseButton
                lock
                type="primary"
                icon="i-ant-design-search-outlined"
                @click="search"
            >
                查询
            </BaseButton>
            <BaseButton
                lock
                plain
                icon="i-ep-refresh-left"
                @click="reset"
            >
                重置
            </BaseButton>
        </div>
    </div>
</template>

<script lang="jsx" setup>
import { computed, toValue, useAttrs, useSlots, watch } from 'vue'
import { useToggle } from '@vueuse/core'
import { useState } from '../composables'
import BaseRender from './BaseRender.vue'
import BaseSelect from './BaseSelect.vue'

const props = defineProps({
    formList: {
        type: Array,
        default: () => []
    },
    defaultFormData: {
        type: [Function, Object],
        default: () => ({})
    },
    handleSearch: {
        type: Function,
        default: () => {}
    },
    handleReset: {
        type: Function,
        default: () => {}
    },
    expose: {
        type: Object,
        default: () => ({})
    }
})

const attrs = useAttrs()
const slots = useSlots()

const emit = defineEmits(['search', 'reset'])

const [isExpand, toggleExpand] = useToggle(false)

const {
    state: formData,
    resetState: resetFormData,
    refreshCache: refreshFormDataCache
} = useState({})

watch(
    () => props.defaultFormData,
    () => {
        refreshFormDataCache(toValue(props.defaultFormData))
        resetFormData()
    },
    {
        immediate: true,
        deep: true
    }
)

const search = () => {
    props.handleSearch({ formData, resetFormData })
    emit('search', formData.value)
}

const reset = () => {
    resetFormData()
    props.handleReset({ formData, resetFormData })
    emit('reset', formData.value)
}

const allColumnNum = computed(() => {
    let inputCount = 0
    let rangeCount = 0

    const list = props.formList.slice(0, 4)
    list.forEach(item => {
        if (isRangePicker(item)) {
            rangeCount += 1
        } else {
            inputCount += 1
        }
    })

    const result = rangeCount * 2 + inputCount

    return result
})

const gridTemplateColumnNum = computed(() => {
    const result = allColumnNum.value
    return result >= 4 ? 4 : result
})

const needShow = (item, index) => {
    if (isExpand.value) return true
    const list = props.formList.slice(0, index)
    if (list.length >= 4) return false

    let count = 0
    list.forEach(item => {
        if (isRangePicker(item)) {
            count += 2
        } else {
            count += 1
        }
    })
    return count < 4 ? true : false
}

const isRangePicker = item => {
    if (item.type !== 'date-picker') return
    const {
        props: { type = '' }
    } = item
    if (type.endsWith('range')) return true
    else return false
}

const addGridColumnStyle = () => {
    props.formList.forEach((item, index) => {
        const isRange = isRangePicker(item)
        if (index === 0) {
            item.gridColumnStart = 1
            if (isRange) {
                item.gridColumnEnd = 3
            } else {
                item.gridColumnEnd = 2
            }
            return
        }
        const prevItem = props.formList[index - 1]
        const start = prevItem.gridColumnEnd % 4
        item.gridColumnStart = start
        if (isRange) {
            item.gridColumnEnd = start + 2
        } else {
            item.gridColumnEnd = start + 1
        }
    })
    props.formList.forEach((item, index) => {
        item.style = Object.assign(
            {},
            {
                gridColumn: `${item.gridColumnStart} / ${item.gridColumnEnd}`
            },
            item.style
        )
        delete item.gridColumnStart
        delete item.gridColumnEnd
    })
}

watch(
    () => props.formList.length,
    () => {
        addGridColumnStyle()
    },
    {
        immediate: true
    }
)

const filterBindMap = {
    defaultFilterBind: (item = {}) => {
        const { ...args } = item
        return args
    }
}

const context = {
    formData,
    resetFormData,
    refreshFormDataCache
}

defineExpose(context)
Object.assign(props.expose, context)
</script>

<style lang="scss" scoped>
.base-search-form {
    --base-search-form-item-input-width: 206px;
    --base-search-form-item-gap: 12px;
    --base-search-form-item-range-picker-width:
        calc(
            2 * var(--base-search-form-item-input-width) + var(--base-search-form-item-gap)
        );

    display: flex;
    transition: all 0.3s;

    .search-form-wrapper {
        flex-shrink: 0;

        :deep() {
            gap: var(--base-search-form-item-gap);
            display: grid;
            grid-template-columns: repeat(var(--grid-template-column-num), 1fr);

            .search-form-item-input {
                width: var(--base-search-form-item-input-width);
            }

            .search-form-item-range-picker {
                width: var(--base-search-form-item-range-picker-width);
            }
        }
    }

    .toggle-box {
        margin-left: 10px;

        .toggle-btn {
            display: flex;
            align-items: center;
            gap: 2px;
            height: 32px;
            user-select: none;
            cursor: pointer;
            white-space: nowrap;
            color: var(--el-color-primary);

            i {
                transition: all 0.15s;
            }

            &.is-expand {
                i {
                    transform: rotate(180deg);
                }
            }
        }
    }

    .button-list {
        margin-left: 10px;
        display: flex;
        white-space: nowrap;
    }
}
</style>